﻿using System.Text;

namespace wfVigenereFullEncoder
{
    public static class Decoder
    {
        public static void DecryptString(string? shifr, string? key, string? alphabet, out string text)
        {
            text = "";
            if (shifr is null || key is null || alphabet is null)
            {
                throw new ArgumentNullException(paramName: "Null reference");
            }

            if (key.Length == 0 || alphabet.Length == 0)
            {
                text = shifr;
                return;
            }

            //Содержит номера букв ключа относительно алфавита, считается, что буквы в алфавите не повторяются
            List<int> codes = new List<int>();
            int index;
            foreach (char i in key)
            {
                index = alphabet.IndexOf(i);

                if (index == -1) throw new ArgumentNullException(paramName: "Symbol in key non contains in text");

                codes.Add(index);
            }

            //Формируем расшифрованный текст
            var builder = new StringBuilder(shifr.Length);

            int indexKey = 0; // Номер следующей буквы ключа
            int lengthKey = codes.Count; // Количество символов в ключе
            int lengthAlphabet = alphabet.Length; // Количество символов в алфавите

            int buf;
            char ch;
            foreach (char i in shifr)
            {
                index = alphabet.IndexOf(i); // Номер символа в алфавите

                if (index > -1)
                {
                    buf = index - codes[indexKey];

                    if (buf < 0)
                    {
                        buf = lengthAlphabet - (buf % lengthAlphabet) * -1;
                    }
                    ch = alphabet[buf];

                    indexKey = (indexKey + 1) % lengthKey;

                    builder.Append(ch);
                }
                else
                {
                    builder.Append(i);
                }
            }

            text = builder.ToString();

        }

        public static void DecryptFile(string? filePathRead, string? filePathWrite, string? key, string? alphabet, int bytes = 1024)
        {
            if (bytes < 4) throw new ArgumentException("Количество байт не может быть меньше 4");

            if (filePathRead is null || filePathWrite is null || key is null || alphabet is null)
            {
                throw new ArgumentException("Null reference");
            }

            if (key.Length == 0 || alphabet.Length == 0)
            {
                throw new ArgumentException("Пустой ключ или алфавит");
            }

            if (!File.Exists(filePathRead))
            {
                throw new ArgumentException($"Файл {filePathRead} не открывается");
            }

            if (!File.Exists(filePathWrite))
            {
                throw new ArgumentException($"Файл {filePathWrite} не открывается");
            }

            //Содержит номера букв ключа относительно алфавита, считается, что буквы в алфавите не повторяются
            List<int> codes = new List<int>();
            int index;
            foreach (char i in key)
            {
                index = alphabet.IndexOf(i);

                if (index == -1) throw new ArgumentException("Symbol in key non contains in text");

                codes.Add(index);
            }

            using FileStream fileText = new FileStream(filePathRead, FileMode.Open, FileAccess.Read);
            using FileStream fileCryptoText = new FileStream(filePathWrite, FileMode.Open, FileAccess.Write);

            byte[] bufferRead = new byte[bytes];
            byte[] bufferWrite = new byte[bytes];

            int indexKey = 0; // Номер следующей буквы ключа
            int lengthKey = codes.Count; // Количество символов в ключе
            int lengthAlphabet = alphabet.Length; // Количество символов в алфавите

            int buf;
            char ch;

            // Читаем по 1Кб
            while (fileText.Read(bufferRead, 0, bytes) > 0)
            {
                string str = System.Text.Encoding.UTF8.GetString(bufferRead);

                //Формируем зашифрованный текст
                var builder = new StringBuilder(str.Length);

                foreach (char i in str)
                {
                    index = alphabet.IndexOf(i); // Номер символа в алфавите

                    if (index > -1)
                    {
                        buf = index - codes[indexKey];

                        if (buf < 0)
                        {
                            buf = lengthAlphabet - (buf % lengthAlphabet) * -1;
                        }
                        ch = alphabet[buf];

                        indexKey = (indexKey + 1) % lengthKey;

                        builder.Append(ch);
                    }
                    else
                    {
                        builder.Append(i);
                    }
                }

                bufferWrite = System.Text.Encoding.UTF8.GetBytes(builder.ToString());

                fileCryptoText.Write(bufferWrite, 0, bytes);
            }

        }
    }
}
